from datetime import datetime, timedelta
from sqlalchemy import Column, Integer, String, DateTime, ForeignKey, CheckConstraint, Index
from sqlalchemy.orm import relationship
from werkzeug.security import generate_password_hash, check_password_hash
from extension import db  # 导入你的 Flask-SQLAlchemy 实例（如 db = SQLAlchemy(app)）

# ------------------------------ 1. 基础关联表（多对多中间表）------------------------------
# 角色-权限中间表（无需单独模型，通过 secondary 关联）
role_permissions = db.Table(
    'role_permissions',
    Column(
        'role_id',
        Integer,
        ForeignKey('roles.id', ondelete='CASCADE'),
        primary_key=True,
        comment='角色ID'
    ),
    Column(
        'permission_id',
        Integer,
        ForeignKey('permissions.id', ondelete='CASCADE'),
        primary_key=True,
        comment='权限ID'
    ),
    Column(
        'created_at',
        DateTime,
        default=datetime.utcnow,
        comment='关联创建时间'
    ),
    comment='角色-权限关联表（多对多）'
)


# ------------------------------ 2. 权限与角色相关模型------------------------------
# 权限模型（对应 permissions 表）
class Permission(db.Model):
    __tablename__ = 'permissions'

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
        comment='权限ID'
    )
    name = Column(
        String(100),
        unique=True,
        nullable=False,
        comment='权限标识（如 user:create、book:read）'
    )
    description = Column(
        String(255),
        comment='权限描述（如“创建系统用户”“查看图书列表”）'
    )
    created_at = Column(
        DateTime,
        default=datetime.utcnow,
        comment='创建时间'
    )
    updated_at = Column(
        DateTime,
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        comment='修改时间'
    )

    # 关联：权限 -> 角色（多对多反向关联）
    roles = relationship('Role', secondary=role_permissions, back_populates='permissions')

    def __repr__(self):
        return f"<Permission {self.name}>"

    __table_args__ = {'comment': '权限表（系统操作权限定义）'}


# 角色模型（对应 roles 表）
class Role(db.Model):
    __tablename__ = 'roles'

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
        comment='角色ID'
    )
    name = Column(
        String(50),
        unique=True,
        nullable=False,
        comment='角色名称（如 admin、editor、viewer）'
    )
    description = Column(
        String(255),
        comment='角色描述（如“系统管理员，拥有所有权限”）'
    )
    created_at = Column(
        DateTime,
        default=datetime.utcnow,
        comment='创建时间'
    )
    updated_at = Column(
        DateTime,
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        comment='修改时间'
    )

    # 关联1：角色 -> 用户（一对多，一个角色对应多个用户）
    users = relationship('User', back_populates='role')
    # 关联2：角色 -> 权限（多对多，通过 role_permissions 中间表）
    permissions = relationship('Permission', secondary=role_permissions, back_populates='roles')

    def __repr__(self):
        return f"<Role {self.name}>"

    __table_args__ = {'comment': '角色表（权限集合，用于用户权限控制）'}


# ------------------------------ 3. 系统用户模型------------------------------
# 用户模型（对应 users 表）
class User(db.Model):
    __tablename__ = 'users'

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
        comment='用户ID'
    )
    username = Column(
        String(50),
        unique=True,
        nullable=False,
        comment='用户名（登录唯一标识）'
    )
    password_hash = Column(
        String(256),
        nullable=False,
        comment='密码哈希（不存储明文）'
    )
    role_id = Column(
        Integer,
        ForeignKey('roles.id', ondelete='SET NULL'),
        comment='角色ID（关联 roles 表，NULL表示无角色）'
    )
    name = Column(
        String(100),
        comment='姓名'
    )
    gender = Column(
        Integer,
        comment='性别编码：0-未知 1-男 2-女 3-其他'
    )
    age = Column(
        Integer,
        comment='年龄（1-100岁）'
    )
    phone = Column(
        String(20),
        comment='电话号码（支持手机号/固定电话）'
    )
    email = Column(
        String(100),
        comment='电子邮箱（用于找回密码）'
    )
    avatar = Column(
        String(255),
        comment='用户头像URL或服务器存储路径'
    )
    status = Column(
        Integer,
        default=1,
        comment='账号状态编码：0-禁用 1-正常'
    )
    created_at = Column(
        DateTime,
        default=datetime.utcnow,
        comment='创建时间'
    )
    updated_at = Column(
        DateTime,
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        comment='修改时间'
    )

    # 关联：用户 -> 角色（多对一，一个用户对应一个角色）
    role = relationship('Role', back_populates='users')

    # ------------------------------ 核心业务方法 ------------------------------
    # 密码加密存储
    def set_password(self, password):
        self.password_hash = generate_password_hash(password)

    # 密码验证
    def verify_password(self, password):
        return check_password_hash(self.password_hash, password)

    # 权限校验：判断用户是否拥有指定权限
    def has_permission(self, permission_name):
        if not self.role:  # 无角色则无权限
            return False
        # 遍历角色下的所有权限，匹配权限标识
        return any(perm.name == permission_name for perm in self.role.permissions)

    # 判断是否为管理员（角色名称为 'admin'）
    def is_admin(self):
        return self.role and self.role.name == 'admin'

    def __repr__(self):
        return f"<User {self.username} (Role: {self.role.name if self.role else 'None'})>"

    # 表级约束与索引
    __table_args__ = (
        CheckConstraint('age > 0 AND age < 150', name='check_user_age'),  # 年龄范围约束
        Index('idx_user_username', 'username'),  # 用户名索引（加速登录查询）
        Index('idx_user_role_id', 'role_id'),  # 角色ID索引（加速按角色筛选）
        {'comment': '系统用户表（管理员、编辑等内部用户）'}
    )


# ------------------------------ 4. 图书分类模型------------------------------
class Category(db.Model):
    __tablename__ = 'category'

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
        comment='分类ID'
    )
    name = Column(
        String(100),
        nullable=False,
        comment='分类名称（如“文学小说”“计算机科学”）'
    )
    code = Column(
        String(50),
        unique=True,
        nullable=False,
        comment='分类编码（唯一标识，如“LIT-001”“CS-002”）'
    )
    created_at = Column(
        DateTime,
        default=datetime.utcnow,
        comment='创建时间'
    )
    updated_at = Column(
        DateTime,
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        comment='修改时间'
    )

    def __repr__(self):
        return f"<Category {self.name} (Code: {self.code})>"

    # 表级索引
    __table_args__ = (
        Index('idx_category_name', 'name'),  # 分类名称索引（模糊查询）
        Index('idx_category_code', 'code'),  # 分类编码索引（加速图书关联）
        {'comment': '图书分类表（用于图书按分类管理）'}
    )


# ------------------------------ 5. 读者模型（借阅用户）------------------------------
class Reader(db.Model):
    __tablename__ = 'readers'

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
        comment='读者ID'
    )
    reader_no = Column(
        String(50),
        unique=True,
        nullable=False,
        comment='读者编号（唯一标识，如“RD-2025-0001”）'
    )
    name = Column(
        String(100),
        nullable=False,
        comment='读者姓名'
    )
    gender = Column(
        Integer,
        default=0,
        comment='性别编码：0-未知 1-男 2-女 3-其他'
    )
    age = Column(
        Integer,
        comment='年龄（1-100岁）'
    )
    phone = Column(
        String(20),
        comment='电话号码（用于逾期提醒）'
    )
    reader_type = Column(
        Integer,
        nullable=False,
        default=0,
        comment='读者类型编码：0-普通读者 1-学生读者 2-教师读者'
    )
    created_at = Column(
        DateTime,
        default=datetime.utcnow,
        comment='创建时间'
    )
    updated_at = Column(
        DateTime,
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        comment='修改时间'
    )

    # 关联：读者 -> 借阅记录（一对多，一个读者对应多条借阅记录）
    borrow_records = relationship('BorrowRecord', back_populates='reader', cascade='all, delete-orphan')

    # ------------------------------ 核心业务方法 ------------------------------
    # 获取读者当前在借的图书数量
    def get_borrowing_count(self):
        return BorrowRecord.query.filter(
            BorrowRecord.reader_id == self.id,
            BorrowRecord.status == 0  # 0-借阅中
        ).count()

    # 判断读者是否有逾期未还的图书
    def has_overdue_books(self):
        return BorrowRecord.query.filter(
            BorrowRecord.reader_id == self.id,
            BorrowRecord.status == 2  # 2-已逾期
        ).first() is not None

    # 获取读者的借阅历史（按借阅时间倒序）
    def get_borrow_history(self):
        return BorrowRecord.query.filter(
            BorrowRecord.reader_id == self.id
        ).order_by(BorrowRecord.borrow_date.desc()).all()


    def __repr__(self):
        return f"<Reader {self.name} (No: {self.reader_no})>"

    # 表级约束与索引
    __table_args__ = (
        CheckConstraint('age > 0 AND age < 100', name='check_reader_age'),  # 年龄范围约束
        Index('idx_reader_no', 'reader_no'),  # 读者编号索引（唯一查询）
        Index('idx_reader_name', 'name'),  # 读者姓名索引（模糊查询）
        {'comment': '读者表（图书借阅用户，如学生、教师、普通市民）'}
    )


# ------------------------------ 6. 图书模型------------------------------
class Book(db.Model):
    __tablename__ = 'books'

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
        comment='图书ID'
    )
    isbn = Column(
        String(20),
        unique=True,
        nullable=False,
        comment='ISBN编号（国际标准书号，唯一）'
    )
    name = Column(
        String(255),
        nullable=False,
        comment='图书名称'
    )
    author = Column(
        String(100),
        nullable=False,
        comment='作者（多作者用逗号分隔，如“余华,莫言”）'
    )
    publisher = Column(
        String(100),
        comment='出版社'
    )
    category = Column(
        String(255),
        comment='图书分类（关联 category 表的 code，多分类用逗号分隔）'
    )
    created_at = Column(
        DateTime,
        default=datetime.utcnow,
        comment='创建时间'
    )
    updated_at = Column(
        DateTime,
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        comment='修改时间'
    )

    # 关联：图书 -> 借阅记录（一对多，一本图书对应多条借阅记录）
    borrow_records = relationship('BorrowRecord', back_populates='book', cascade='all, delete-orphan')

    # ------------------------------ 核心业务方法 ------------------------------

    # 判断图书是否可借阅（无“借阅中”记录即为可借）
    def is_borrowable(self):
        return BorrowRecord.query.filter(
            BorrowRecord.book_id == self.id,
            BorrowRecord.status == 0  # 0-借阅中
        ).first() is None

    # 获取图书的借阅历史
    def get_borrow_history(self):
        return BorrowRecord.query.filter(
            BorrowRecord.book_id == self.id
        ).order_by(BorrowRecord.borrow_date.desc()).all()

    def __repr__(self):
        return f"<Book {self.name} (ISBN: {self.isbn})>"

    # 表级索引
    __table_args__ = (
        Index('idx_book_isbn', 'isbn'),  # ISBN索引（唯一查询）
        Index('idx_book_name', 'name'),  # 图书名称索引（模糊查询）
        Index('idx_book_category', 'category'),  # 分类索引（按分类筛选）
        {'comment': '图书表（存储图书基础信息）'}
    )


# ------------------------------ 7. 借阅记录模型------------------------------
class BorrowRecord(db.Model):
    __tablename__ = 'borrow_records'

    id = Column(
        Integer,
        primary_key=True,
        autoincrement=True,
        comment='借阅记录ID'
    )
    borrow_no = Column(
        String(50),
        unique=True,
        nullable=False,
        comment='借阅编号（唯一标识，如“BR-2024-0001”）'
    )
    book_id = Column(
        Integer,
        ForeignKey('books.id', ondelete='RESTRICT'),  # 禁止删除有未还记录的图书
        nullable=False,
        comment='图书ID（关联 books 表）'
    )
    reader_id = Column(
        Integer,
        ForeignKey('readers.id', ondelete='RESTRICT'),  # 禁止删除有未还记录的读者
        nullable=False,
        comment='读者ID（关联 readers 表）'
    )
    borrow_date = Column(
        DateTime,
        default=datetime.utcnow,
        nullable=False,
        comment='借阅日期'
    )
    due_date = Column(
        DateTime,
        nullable=False,
        comment='应还日期（根据借阅规则计算，如借阅日+30天）'
    )
    return_date = Column(
        DateTime,
        comment='归还日期（未归还为NULL，归还后更新为当前时间）'
    )
    status = Column(
        Integer,
        nullable=False,
        default=0,
        comment='借阅状态编码：0-借阅中 1-已归还 2-已逾期'
    )
    created_at = Column(
        DateTime,
        default=datetime.utcnow,
        comment='记录创建时间'
    )
    updated_at = Column(
        DateTime,
        default=datetime.utcnow,
        onupdate=datetime.utcnow,
        comment='记录修改时间'
    )

    # 关联关系
    # 借阅记录 -> 图书（多对一）
    book = relationship('Book', back_populates='borrow_records')
    # 借阅记录 -> 读者（多对一）
    reader = relationship('Reader', back_populates='borrow_records')

    # ------------------------------ 核心业务方法 ------------------------------
    def handle_renewal(self, days=30):
        """
        处理图书续借逻辑
        :param days: 续借天数，默认30天
        """
        # 检查当前状态是否允许续借
        if self.status != 0:
            raise ValueError(f"当前状态为{self.get_status_text()}，无法办理续借")


        # 计算新的应还日期
        current_time = datetime.utcnow()
        # 以当前时间或原应还日期中较晚的一个为基准计算新应还日期
        new_due_date = self.due_date + timedelta(days=days)

        # 更新续借相关字段
        self.due_date = new_due_date
        db.session.commit()

        return new_due_date


    def handle_return(self):
        """
        处理图书归还逻辑
        - 仅允许“借阅中”状态的记录办理还书
        - 更新归还日期和借阅状态
        """
        if self.status != 0:
            raise ValueError(f"当前状态为{self.get_status_text()}，无法办理还书")

        self.return_date = datetime.utcnow()
        self.status = 1  # 改为"已归还"状态
        db.session.commit()
        return True

    def check_overdue(self):
        """
        检查并更新逾期状态
        - 仅对“借阅中”状态有效
        - 若当前时间超过应还日期，自动改为“已逾期”状态
        """
        if self.status != 0:
            return False  # 非借阅中状态无需检查

        current_time = datetime.utcnow()
        if current_time > self.due_date:
            self.status = 2  # 改为"已逾期"状态
            db.session.commit()
            return True
        return False

    def get_status_text(self):
        """将状态编码转换为文字描述"""
        status_map = {
            0: "借阅中",
            1: "已归还",
            2: "已逾期"
        }
        return status_map.get(self.status, "未知状态")

    def calculate_overdue_days(self):
        """计算逾期天数（仅对已逾期状态有效）"""
        if self.status != 2:
            return 0

        current_time = datetime.utcnow()
        overdue_delta = current_time - self.due_date
        return overdue_delta.days

    def __repr__(self):
        return f"<BorrowRecord {self.borrow_no} [{self.get_status_text()}]>"

    # 表级索引和约束
    __table_args__ = (
        Index('idx_borrow_book_reader', 'book_id', 'reader_id'),  # 图书+读者联合索引
        Index('idx_status', 'status'),  # 状态索引，加速筛选
        Index('idx_borrow_due_date', 'due_date'),  # 应还日期索引，便于查询逾期
        Index('idx_borrow_date', 'borrow_date'),  # 借阅日期索引，便于按时间查询
        {'comment': '图书借阅记录表（存储图书借阅和归还信息）'}
    )
